/*
 * @author Pablo Rodríguez Fernández
 */

package poker;

import java.util.Scanner;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;
import java.util.Collections;
import java.io.Serializable;
//import prop.Mano;
import prop.Carta;

public class ManoPoker extends Mano implements Serializable {

    private EvalData res;
    private boolean validRes;

    public ManoPoker() {
        cartas = new Vector<Carta>(5);
        numCartasMax = 5;
        validRes = false;
    }

    public ManoPoker(int numMax) {
        cartas = new Vector<Carta>(numMax);
        numCartasMax = numMax;
        validRes = false;
    }

    public class EvalData implements Serializable {
        public Integer[] numc, numr;
            // numc contiene las cartas que aparecen en la mano
            // numr contiene el número de repeticiones de esas cartas
        public int size;        // El número de cartas diferentes en la mano
        public boolean color;   // Si todas las cartas son del mismo palo
    }

    /* Retorna falso si ya se tiene el número máximo de Cartas, en otro caso
        añade la Carta c a la mano. */
    @Override
    public boolean anadirCarta(Carta c) {
        if (cartas.size() < numCartasMax || numCartasMax == 0) cartas.add(c);
        else return false;
        validRes = false;
        return true;
    }
    
    /* Borra las cartas presentes en la mano. */
    @Override
    public void borrarMano() {
        cartas.clear();
        validRes = false;
    }
    
    /* Pre: La Carta c se encuentra en la mano.
        Elimina la carta C y la retorna. */
    @Override
    public Carta quitarCarta(Carta c) {
        if (!cartas.contains(c)) {
            int i = indexOf(c);
            if (i >= 0) c = cartas.get(i);
        }
        cartas.remove(c);
        validRes = false;
        return c;
    }

    public Carta quitarCarta(int i) {
        validRes = false;
        return cartas.remove(i);
    }

    /* Retorna el valor de la jugada */
    @Override
    public int evaluar() {
        int eval = 0;
        if (cartas.size() > 0) {
            conteo();
            
            boolean as = res.numc[0] == 1 || res.numc[1] == 1;
            
            /* Comprobamos el número de comodines que tenemos; identificados
                como 0 por conteo() */
            int jokers = 0;
            if(res.numc[0] == 0) jokers = res.numr[0];
            
            /* Comprobamos escaleras, poker y repoker */
            if (esEscalera(res.numc, res.numr, res.size, jokers)) {
                if (res.size >= 2) {
                    if (as) eval += 1400 + res.numc[res.size-1]; // Por el As
                    else {
                        eval += res.numc[res.size-1]*100 + res.numc[res.size-2];
                    }
                }
                if (esReal(res.numc, res.size, jokers)) {
                        eval = 1400; // Por el As
                        if (res.size == 1 || res.numc[res.size-2] == 0
                            || res.numc[0] == 0)
                            eval += 13;
                        else eval += res.numc[res.size-2];
                        if (res.color) eval += 100000;
                        else eval +=40000; // Escalera
                }
                else if (jokers == 4) { // Repoker
                    eval = res.numc[res.size-1]*100;
                    eval += jugada(5)*10000;
                }
                else if (res.color) eval += 80000; // De Color
                else if (jokers == 3) { // Poker
                    eval = res.numc[res.size-1]*100 + res.numc[res.size-2];
                    eval += jugada(4)*10000;
                }
                else eval +=40000; // Escalera
                return eval;
            }
            
            /* Comprobamos que sea color */
            if (res.color) {
                eval = 50000;
                return eval + maximas(1,res.numc,res.numr,res.size);
            }
            
            /* Hemos descartado cualquier escalera y color */
            int rep = 6-res.size;
            int i, j;
            
            switch (res.size) {
                case 5:
                    // Guarda la carta más alta
                    if (as) eval += 1400 + res.numc[res.size-1]; // Por el As
                    else {
                        eval += res.numc[res.size-1]*100 + res.numc[res.size-2];
                    }
                    // si tiene comodín, pareja
                    if (jokers > 0) eval += jugada(1+jokers);
                break;
                
                case 4:
                    
                    if (jokers == rep) eval = jugada(jokers+rep-1)*10000;
                    else eval = jugada(jokers+rep)*10000;
                        // Con 2 comodines a lo sumo trío
                    eval += maximas(rep,res.numc,res.numr,res.size);
                break;
                
                case 3:
                    i = buscarMax(rep,res.numc,res.numr,res.size);
                        // indice de la carta más repetida
                    
                    switch (res.numr[i]) {
                        case 3:
                            // los casos con 1 joker y un trío ya está tratado
                            if (res.numc[i] == 0 || jokers == 1) {
                                eval = jugada(4)*10000; // poker
                            }
                            else eval = jugada(3)*10000; // trío
                            eval += maximas(rep,res.numc,res.numr,res.size);
                        break;
                        
                        case 2:
                            if (jokers==0) { // doble pareja
                                eval = 20000;
                                for (j = i-1; j >= 0; j--) {
                                    if (res.numr[j] == 2) break;
                                }
                                if (res.numc[j] == 1) {
                                    eval += 1400;
                                    eval += res.numc[i];
                                }
                                else eval += res.numc[i]*100 + res.numc[j];;
                            }
                            else { // full con un joker
                                eval = 60000;
                                eval += maximas(rep,res.numc,res.numr,res.size);
                            }
                        break;
                    }
                    
                break;
                
                case 2: // Full, Repoker o Poker
                    i = buscarMax(rep,res.numc,res.numr,res.size);
                        // indice de la carta más repetida
                    switch (res.numr[i]) {
                        case 3:
                            if (jokers==0) {
                                eval = 60000; // full
                                eval += maximas(rep,res.numc,res.numr,res.size);
                            }
                            else if (jokers == 2) { // Repoker 2 jokers
                                eval = jugada(5)*10000;
                                eval += res.numc[i]*100;
                            }
                            else {
                                eval = jugada(4)*10000; // poker
                                eval += maximas(rep,res.numc,res.numr,res.size);
                            }
                        break;
                        
                        case 4: // Repoker con joker
                            if (jokers > 0) {
                                eval = jugada(5)*10000;
                                eval += res.numc[i]*100;
                            }
                            else {
                                eval = jugada(4)*10000;
                                eval += maximas(rep,res.numc,res.numr,res.size);
                            } 
                        break;
                    }
                break;
            }
        }
        return eval;
    }

    /* Retorna un EvalData.
        Pre: tamaño > 0 */
    public EvalData conteo()
    {
        if (validRes) return res;
        res = new EvalData();
        res.color = true;

        Map<Integer,Integer> m =
        Collections.synchronizedSortedMap(new TreeMap<Integer,Integer>());
        
        // Realizamos un recuento de las cartas repetidas, entre otros
        Carta c = cartas.get(0);
        Carta.Palos palo1 = c.getPalo();
        
        int num = c.getNum();
        if (palo1 == Carta.Palos.COMODIN) {
            num = 0; // Todos los comodines a 0 con tal de agruparlos
        }

        m.put(num, 1);
        for (int i = 1; i < cartas.size(); i++) {
            c = cartas.get(i);
            num = c.getNum();
            Carta.Palos palo = c.getPalo();
            
            if (palo == Carta.Palos.COMODIN) num = 0;
                // Todos los comodines a 0 con tal de agruparlos
            else if (res.color && palo1 == Carta.Palos.COMODIN) palo1 = palo;
            
            /* Vamos mirando si tenemos una jugada de color; los
                comodines no eliminan esta propiedad */
            if (res.color && (palo1 != palo) && palo != Carta.Palos.COMODIN) {
                res.color = false;
            }
            
            // Vamos añadiendo o sumando valores al map
            if (m.containsKey(num)) m.put(num,m.get(num)+1);
            else m.put(num, 1);
        }
        
        // Vector con los números de cartas que tenemos en el map
        res.size = m.size();
        res.numr = new Integer[res.size];
        res.numc = new Integer[res.size];
        
        m.keySet().toArray(res.numc);
        for (int i = 0; i < res.size; i++) res.numr[i] = m.get(res.numc[i]);

        validRes = true;
        return res;
    }

    /* Retorna si es una escalera (real o no); tiene en cuenta comodines */
    private boolean esEscalera
        (Integer[] numc, Integer[] numr, int size, int jokers)
    {
        int diff = 0, i = 1;
        
        if (/*size > 2 && */jokers > 0) i = 2;
            // Para compensar, p.ej. CCA45; C = comodín

        for (int j = i-1; j < size; j++) if (numr[j] > 1) diff = jokers+1;
            // Para evitar que tome CC### como escalera, # = número
        
        for (; i < size; i++) {
            int d = numc[i] - numc[i-1] - 1;
            if (d >= 8) d -= 8; // Compensar CA10QK/CAJQK/CA10JK...
            diff += d;
        }
        
        if (diff == 0 || (jokers >= diff && diff > 0)) return true;
        return false;
    }
    
    /* Retorna si es una escalera real.
        Pre: es escalera */
    private boolean esReal (Integer[] numc, int size, int jokers) {
        boolean inf = false, sup = true;

        int i = 0;
        if (jokers > 0 && size > 2) i++;
        if (numc[0] >= 0 && numc[0] <= 1) inf = true;
        i++;

        for (; i < size; i++) {
            if (numc[i] < 10) sup = false;
        }
        return inf&&sup;
    }

    /* Retorna el número de jugada asociada a numero de cartas */
    private int jugada(int numCartas) {
        switch (numCartas) {
            case 2: return 1;   // Pareja
            case 3: return 3;   // Trío
            case 4: return 7;   // Poker
            case 5: return 9;  // Repoker
            default: return 0;  // Carta más alta
        }
    }

    /* Buscamos puntuación de las cartas más altas */
    private int maximas(int rep, Integer[] numc, Integer[] numr, int size) {
        int i = buscarMax(rep, numc, numr, size) , eval = 0, aux;
        boolean as = numc[0] == 1 || numc[1] == 1;
        
        aux = numc[i];
        if (aux == 1) aux = 14; // As es la carta más alta
        else if (aux == 0) { // Significa que los comodines son los rep
            i = size-1;
            aux = numc[i];
        }
        eval = aux*100;
        
        if (i == size-1 && !as) aux = numc[i-1];
        else if (as && aux != 14) aux = 14;
            // As es la carta más alta; si no lo has contado ya
        else aux = numc[size-1];
        
        return eval + aux;
    }
    
    /* Buscamos índice del máximo repetido */
    private int buscarMax(int rep, Integer[] numc, Integer[] numr, int size) {
        while (true) {
            int i = size-1;
            for (; i >= 0; i--) if (numr[i] == rep) break;
            if (i < 0) rep--;
            else return i;
        }
    }
    
    static Carta.Palos toPalo(int i) {
        
        switch(i) {
            case 1: return Carta.Palos.CORAZON;
            case 2: return Carta.Palos.DIAMANTE;
            case 3: return Carta.Palos.PICA;
            case 4: return Carta.Palos.TREBOL;
            default: return Carta.Palos.COMODIN;
        }
    }
    
    static Estado toEstado(int i) {
        switch(i) {
            case 1: return Estado.JUGANDO;
            default: return Estado.RETIRADO;
        }
    }
    
    public static void main(String[] args) {
        ManoPoker m = new ManoPoker();
        Scanner scanf = new Scanner(System.in);
        String orden;

        orden = scanf.next();
        while (!orden.equals("exit")) {
        
            if (orden.equals("show")) {
                Vector<Carta> v = m.getVector();
                System.out.println("Mostrando Mano: ");
                for (int i = 0; i < v.size(); i++) {
                    System.out.print("Num: ");
                    System.out.print(v.get(i).getNum());
                    System.out.print(" Palo: ");
                    System.out.print(v.get(i).getPalo());
                    System.out.print("\n");
                }
            }
            else if (orden.equals("add")) {
                int i = scanf.nextInt();
                int palo = scanf.nextInt();
                
                m.anadirCarta(new Carta(toPalo(palo), i));
                
            }
            else if (orden.equals("rm")) {
                int i = scanf.nextInt();
                int palo = scanf.nextInt();
                
                m.quitarCarta(new Carta(toPalo(palo), i));
            }
            else if (orden.equals("clear")) {
                m.borrarMano();
            }
            else if (orden.equals("size")) {
                System.out.println(m.getNumCartas());
            }
            else if (orden.equals("sizeMax")) {
                System.out.println(m.getNumCartasMax());
            }
            else if (orden.equals("status")) {
                System.out.println(m.getEstado());
            }
            else if (orden.equals("setst")) {
                int i = scanf.nextInt();
                m.setEstado(toEstado(i));
            }
            else if (orden.equals("eval")) {
                System.out.println(m.evaluar());
            }
            else {
                System.out.println("Lista de órdenes:\n");
                System.out.println("* show - Muestra el contenido de la Mano");
                System.out.print("* add <numCarta> <numPalo># - Función ");
                System.out.println("anadirCarta(new Carta(numPalo, numCarta))");
                System.out.print("* rm <numCarta> <numPalo># - quitarCarta()");
                System.out.println(" siguiendo el mismo estilo que add");
                System.out.println("* size - getNumCartas()");
                System.out.println("* sizeMax - getNumCartasMax()");
                System.out.println("* status - getEstado()");
                System.out.print("* setst <numEstado>## -");
                System.out.println(" setEstado(numEstado)");
                System.out.println("* clear - borrarMano()");
                System.out.println("* eval - evaluar()");
                System.out.println("* exit - Cierra el programa\n");
                
                System.out.println("# Id de los palos:");
                System.out.println("    0 - COMODIN");
                System.out.println("    1 - CORAZON");
                System.out.println("    2 - DIAMANTE");
                System.out.println("    3 - PICA");
                System.out.println("    4 - TREBOL\n");
                
                System.out.println("## Id de los estados:");
                System.out.println("    0 - RETIRADO");
                System.out.println("    1 - JUGANDO");
            }
            orden = scanf.next();
        }
    }
};
